﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MetaModel.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the MetaModel type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.ComponentModel.Modelling
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    public class MetaModel
    {
        #region Fields

        private static MetaModel _default;
        private readonly ReaderWriterLock _modelTypesLock = new ReaderWriterLock();
        private readonly IDictionary<Type, MetaModelType> _modelTypes = new Dictionary<Type, MetaModelType>();
        private readonly IMetaModelAttributeProvider _attributeProvider;
        private MetaModelType _cachedType;

        #endregion

        public MetaModel() : this(null)
        {
        }

        public MetaModel(IMetaModelAttributeProvider attributeProvider)
        {
            _attributeProvider = attributeProvider ?? new DefaultMetaModelAttributeProvider();
        }
      
        public IMetaModelAttributeProvider AttributeProvider
        {
            get { return _attributeProvider; }
        }

        private MetaModelType CreateType(Type type)
        {
            var modelType = new MetaModelType(this, type);

            _modelTypesLock.AcquireWriterLock(-1);
            try
            {
                // verify the model type has not been created on a different thread
                MetaModelType result;
                if (!_modelTypes.TryGetValue(type, out result))
                {
                    result = modelType;
                    _modelTypes.Add(type, result);
                }
                
                return _cachedType = result;
            }
            finally
            {
                _modelTypesLock.ReleaseWriterLock();
            }
        }

        public MetaModelType GetOrCreateType(Type type)
        {
            return GetType(type) ?? CreateType(type);
        }

        public MetaModelType GetType(Type type)
        {
            var result = _cachedType;
            if (result != null && result.ReflectedType.Equals(type))
            {
                return result;
            }

            _modelTypesLock.AcquireReaderLock(-1);
            try
            {
                _modelTypes.TryGetValue(type, out result);
                if (result != null)
                {
                    _cachedType = result;
                }
                
                return result;
            }
            finally
            {
                _modelTypesLock.ReleaseReaderLock();
            }
        }

        public static MetaModel Default
        {
            get
            {
                if (_default == null)
                {
                    Interlocked.CompareExchange(ref _default, new MetaModel(), null);
                }

                return _default;
            }
        }

#if SILVERLIGHT

        internal class ReaderWriterLock
        {
            private readonly object _lockObject = new object();

            public void ReleaseWriterLock()
            {
                Monitor.Exit(_lockObject);
            }

            public void AcquireWriterLock(int i)
            {
                Monitor.Enter(_lockObject);
            }

            public void AcquireReaderLock(int i)
            {
                Monitor.Enter(_lockObject);
            }

            public void ReleaseReaderLock()
            {
                Monitor.Exit(_lockObject);
            }
        }

#endif
    }
}
